/****** BEGIN LICENSE BLOCK *****
 Version: MPL 1.1/GPL 2.0/LGPL 2.1

 The contents of this file are subject to the Mozilla Public License Version
 1.1 (the "License"); you may not use this file except in compliance with
 the License. You may obtain a copy of the License at 
 http://www.mozilla.org/MPL/

 Software distributed under the License is distributed on an "AS IS" basis,
 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 for the specific language governing rights and limitations under the
 License.

 The Original Code is 3liz code,

 The Initial Developer of the Original Code is René-Luc D'Hont rldhont@3liz.com
 Portions created by the Initial Developer are Copyright (C) 2009
 the Initial Developer. All Rights Reserved.

 Some Parts of the Code are taken from the original Mozilla Firefox and/or XulRunner Code. 
 If these parts were subject to a licence not in compliance with the License, 
 it is not intentionnal and please contact the Initial Developer.

 Alternatively, the contents of this file may be used under the terms of
 either of the GNU General Public License Version 2 or later (the "GPL"),
 or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 in which case the provisions of the GPL or the LGPL are applicable instead
 of those above. If you wish to allow use of your version of this file only
 under the terms of either the GPL or the LGPL, and not to allow others to
 use your version of this file under the terms of the MPL, indicate your
 decision by deleting the provisions above and replace them with the notice
 and other provisions required by the GPL or the LGPL. If you do not delete
 the provisions above, a recipient may use your version of this file under
 the terms of any one of the MPL, the GPL or the LGPL.

 ***** END LICENSE BLOCK ***** */

/**
 * @fileoverview geoloc:// Protocol Handler
 */

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");

// Preferences
Components.utils.import("resource://geolocater/geolocater-prefs.js");
var prefs = GeolocaterPrefs;

// URI parsing
Components.utils.import("resource://geolocater/URI.js");

const Ci = Components.interfaces;
const Cc = Components.classes;

//Protocol handler
function GeolocProtocolHandler(){
}

GeolocProtocolHandler.prototype = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler,Ci.nsIObserver]),
  classDescription: "Geoloc Protocol Handler",
  classID: Components.ID("{304ca79d-e830-45e4-b12e-7bbab2aa2496}"),
  contractID: "@mozilla.org/network/protocol;1?name=geoloc",

  _PHIID: Ci.nsIProtocolHandler,
  get scheme()          { return "geoloc"; },
  get protocolFlags()   { return this._PHIID.URI_NORELATIVE | this._PHIID.URI_NOAUTH },
  get defaultPort()     { return 0; },
  
  allowPort: function (aPort, aScheme)
  {
    return false;
  },
  
  newURI: function (aSpec, aOriginalCharset, aBaseURI)
  {
    var uri = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIStandardURL);
    uri.init(Ci.nsIStandardURL.URLTYPE_STANDARD,6667,aSpec,aOriginalCharset,aBaseURI);

    return uri.QueryInterface(Ci.nsIURI);
  },
  
  newChannel: function (aURI)
  {
	dump("aURI's path is: " + aURI.path + "\n");
    var ioService = Cc["@mozilla.org/network/io-service;1"]
       .getService(Ci.nsIIOService);
    
    //var pageURI = "https://www.google.com/loc/json";
    var baseURI = Services.prefs.
      getCharPref("extensions.geolocater.googleservice");
	
    //pageURI += '?browser=firefox&sensor=true';
	var pageURI = baseURI + aURI.path.substr(aURI.path.indexOf("?"));
	dump("pageUri is: " + pageURI + "\n");
	
    if (aURI.host == "www.google.com")
	{
	  dump("Normal service provider called.\n");
	  
	  var privacyLevel = Services.prefs.getCharPref("extensions.geolocater.privacylevel");
	  dump("Privacy level is: " + privacyLevel + "\n");
   
	  // High privacy - only IP should be available to provider
	  if (privacyLevel == 2)
	  {
		// TODO: Unuglify (far too much code duplication, etc)
		dump("Trying to manipulate provider.\n");
		
		// Create the pipe
		var pipe = Cc["@mozilla.org/pipe;1"].
		  createInstance(Ci.nsIPipe);
		pipe.init(true,true, 0, 0, null);

		// Create the channel
		var channel = Cc["@mozilla.org/network/input-stream-channel;1"].
		  createInstance(Ci.nsIInputStreamChannel);
		channel.setURI(aURI);
		channel.contentStream = pipe.inputStream;
		channel.QueryInterface(Ci.nsIChannel);
		channel.contentType = "application/json";
		channel.contentCharset = "UTF-8";

		dump("Querying provider.\n");
		xhr = queryGoogleService(null, baseURI + '?browser=firefox&sensor=true');
		dump("Provider queried.\n");
		
		var resp = {};
		try {
			dump("Trying to parse response: " + xhr.responseText + "\n");
			resp = JSON.parse(xhr.responseText);
			dump("Response parsed.\n");
		} catch(e) { dump("Exception raised parsing provider response: " + e + "\n");}
		
		var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
  	    createInstance(Ci.nsIConverterOutputStream);
		converter.init(pipe.outputStream, "UTF-8", 0, 0);
		var coords = resp.location;
		data = JSON.stringify(resp);
		converter.writeString(data);
		converter.close();
		dump(data+'\n');
		
		pipe.outputStream.close();

		dump("Returning manipulated channel.\n");
		return channel;
	  }
	  
	  // Medium privacy - all data except WiFi sent to provider
	  // For now, this means the same as high level, as there isn't any location data other than WiFi and IP in Firefox
	  // This may change sometime in the future, with the addition of cell tower measurements and so forth
	  if (privacyLevel == 1)
	  {
		var newURI = new URI(pageURI);
		var query = newURI.parseQuery();
		
		delete query.params["wifi"];
		newURI.setQuery(query.toString());
		
		dump("New provider URI: " + newURI.toString() + "\n");
		return ioService.newChannel(newURI, null, null);
	  }
	  
	  // Low privacy - all available data sent to provider, no modification on our part
      return ioService.newChannel(pageURI, null, null);
	}

	dump("Returning default location.");
    var id = aURI.path.split('?')[0].replace('/','');
    var localhost = Services.prefs.
      getCharPref("extensions.geolocater.localhost");
    localhost = JSON.parse(localhost);

    // Create the pipe
    var pipe = Cc["@mozilla.org/pipe;1"].
      createInstance(Ci.nsIPipe);
    pipe.init(true,true, 0, 0, null);

    // Create the channel
    var channel = Cc["@mozilla.org/network/input-stream-channel;1"].
      createInstance(Ci.nsIInputStreamChannel);
    channel.setURI(aURI);
    channel.contentStream = pipe.inputStream;
    channel.QueryInterface(Ci.nsIChannel);
    channel.contentType = "application/json";
    channel.contentCharset = "UTF-8";

    var converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
      createInstance(Ci.nsIConverterOutputStream);
    converter.init(pipe.outputStream, "UTF-8", 0, 0);
    var coords = localhost[id].coords
    var data = JSON.stringify({
      'status':'OK',
      'accuracy':coords.accuracy,
      'location':{'lat':coords.latitude,'lng':coords.longitude},
      'access_token':id
    });
    converter.writeString(data);
    converter.close();
    dump(data+'\n');

    pipe.outputStream.close();

    return channel;
  },
  observe: function(aSubject, aTopic, aData) {
    Components.utils.import("resource://geolocater/geolocater-prefs.js");
  },
  register: function() {
    var observerService = Cc["@mozilla.org/observer-service;1"]
      .getService(Ci.nsIObserverService);
    observerService.addObserver(this, "profile-after-change", false);
  },
  unregister: function() {
    var observerService = Cc["@mozilla.org/observer-service;1"]
      .getService(Ci.nsIObserverService);
    observerService.removeObserver(this, "profile-after-change");
  }
};

var components = [GeolocProtocolHandler];

/**
* XPCOMUtils.generateNSGetFactory was introduced in Mozilla 2 (Firefox 4).
* XPCOMUtils.generateNSGetModule is for Mozilla 1.9.2 (Firefox 3.6).
*/
if (XPCOMUtils.generateNSGetFactory)
    var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
else
    var NSGetModule = XPCOMUtils.generateNSGetModule(components);

function queryGoogleService(aAccessPoints, baseUri) {
	dump("Entering provider querying function.\n");
    var CL = Components.classes;
    var CI = Components.interfaces;

    var xhr = CL["@mozilla.org/xmlextras/xmlhttprequest;1"]
      .createInstance(CI.nsIXMLHttpRequest);
    xhr.mozBackgroundRequest = true;

    var gsUrl = prefs.googserv;
	dump("Opening HTTP request...\n");
    xhr.open("POST", /*gsUrl*/baseUri, false);
	dump("Request opened.\n");
    xhr.channel.loadFlags = CI.nsIChannel.LOAD_ANONYMOUS;
    xhr.onerror = function(req) {
      alert('error');
    };

    var request = {version:"1.1.0",request_address:true};

    if (aAccessPoints != null) {
      request.wifi_towers = aAccessPoints.map(function (ap) ({
            mac_address: ap.mac,
            ssid: ap.ssid,
            signal_strength: ap.signal
            }));
    }

    var jsonString = JSON.stringify(request);
	dump("Sending request to provider: " + jsonString + "\n");
    xhr.send(jsonString);
	dump("Request returned.\n");
	return xhr;
  }